package com.example.online_gobang.game;

import com.example.online_gobang.OnlineGobangApplication;
import com.example.online_gobang.mapper.UserMapper;
import com.example.online_gobang.model.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.UUID;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 74646
 * Date: 2022-11-20
 * Time: 15:27
 */
// 这个类表示一个游戏房间
@Data
public class Room {
    private String roomId;
    private User user1;
    private User user2;
    private int whiteUser; // 先手方的玩家id

    private static final int ROW = 15;
    private static final int COL = 15;

    private OnlineUserManager onlineUserManager;

    // 引入 roomManager 用于房间销毁
    private RoomManager roomManager;

    // 用于将 JSON 格式的字符串转换 Java 对象
    private ObjectMapper objectMapper = new ObjectMapper();

    // 引入 UserMapper，用于更新比赛数据
    private UserMapper userMapper;

    // 这个二维数组表示棋盘
    // 1) 使用 0 表示当前位置未落子. 初始化好的 int 二维数组, 就相当于是 全 0
    // 2) 使用 1 表示 user1 的落子位置
    // 3) 使用 2 表示 user2 的落子位置
    private int[][] board = new int[ROW][COL];

    public Room() {
        // 构造 Room 的时候生成一个唯一的字符串表示房间 id.
        // 使用 UUID 来作为房间 id
        roomId = UUID.randomUUID().toString();
        // 通过入口类中记录的 context 来手动获取到前面的 RoomManager 和 OnlineUserManager
        onlineUserManager = OnlineGobangApplication.context.getBean(OnlineUserManager.class);
        roomManager = OnlineGobangApplication.context.getBean(RoomManager.class);
        userMapper = OnlineGobangApplication.context.getBean(UserMapper.class);
    }

    // 处理一次落子的操作
    public void putChess(String payload) throws IOException {
        // 1. 记录当前落子的位置
        // 将 json 格式的字符串转换成 Java 对象
        GameRequest request = objectMapper.readValue(payload,GameRequest.class);
        GameResponse response = new GameResponse();
        // 当前这个子是玩家1 落的还是玩家2 落的. 根据这个玩家1 和 玩家2 来决定往数组中是写 1 还是 2
        int chess = request.getUserId() == user1.getUserId() ? 1 : 2;
        int row = request.getRow();
        int col = request.getCol();
        if (board[row][col] != 0) {
            System.out.println("当前位置: ("+row+" ," + col+" )" +"已经有子了");
            return;
        }
        board[row][col] = chess;

        // 2. 打印出当前的棋盘信息, 方便来观察局势. 也方便后面验证胜负关系的判定.
//        printBoard();

        // 3. 进行胜负判定
        int winner = checkWinner(row, col, chess);

        // 4. 给房间里的所有客户端返回响应
        response.setMessage("putChess");
        response.setUserId(request.getUserId());
        response.setRow(row);
        response.setCol(col);
        response.setWinner(winner);

        // 要想给用户发送 websocket 数据, 就需要获取到这个用户的 WebSocketSession
        WebSocketSession session1 = onlineUserManager.getFromGameRoom(user1.getUserId());
        WebSocketSession session2 = onlineUserManager.getFromGameRoom(user2.getUserId());

        // 万一当前查到的会话为空(玩家已经下线了) 特殊处理一下
        if (session1 == null) {
            // 玩家1 已经下线了. 直接认为玩家2 获胜!
            response.setWinner(user2.getUserId());
            System.out.println("玩家1 掉线!");
        }
        if (session2 == null) {
            // 玩家2 已经下线. 直接认为玩家1 获胜!
            response.setWinner(user1.getUserId());
            System.out.println("玩家2 掉线!");
        }
        // 把响应构造成 JSON 字符串, 通过 session 进行传输
        if (session1 != null) {
            session1.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
        if (session2 != null) {
            session2.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }

        // 5. 如果当前胜负已分, 此时这个房间就失去存在的意义了. 就可以直接销毁房间. (把房间从房间管理器中给移除)
        if (response.getWinner() != 0) {
            // 胜负已分
            System.out.println("游戏结束! 房间即将销毁! roomId=" + roomId + " 获胜方为: " + response.getWinner());
            // 更新获胜方和失败方的信息.
            int winUserId = response.getWinner();
            int loseUserId = response.getWinner() == user1.getUserId() ? user2.getUserId() : user1.getUserId();
            userMapper.userWin(winUserId);
            userMapper.userLose(loseUserId);
            // 销毁房间
            roomManager.remove(roomId, user1.getUserId(), user2.getUserId());
        }
    }

    // 打印棋盘内容
    private void printBoard() {
        System.out.println("打印棋盘信息, 当前房间: " + roomId);
        System.out.println("====================================================");
        for (int i = 0; i < ROW; i++) {
            for (int j = 0; j < COL; j++) {
                System.out.print(board[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println("====================================================");
    }


    // 使用这个方法来判定当前落子是否分出胜负.
    // 约定如果玩家1 获胜, 就返回玩家1 的 userId
    // 如果玩家2 获胜, 就返回玩家2 的 userId
    // 如果胜负未分, 就返回 0
    private int checkWinner(int row, int col, int chess) {
        //  判断当前是谁获胜
        // 1. 一行五子连珠
        for (int c = col -4; c <= col && c <= COL-5; c++) {
            try{
                    if (board[row][c] == chess
                            && board[row][c +1] == chess
                            && board[row][c +2] == chess
                            && board[row][c +3] == chess
                            && board[row][c +4] == chess) {
                        // 构成五子连珠，chess == 1 获胜者是玩家1；chess == 2 获胜者是玩家2
                        return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e){
                // 如果出现数组下标越界的情况，就忽略这个异常
                continue;
            }
        }
        // 2. 一列五子连珠
        for (int r = row - 4; r <= row && r <= ROW-5; r++) {
            try{
                if (board[r][col] == chess
                        && board[r +1][col] == chess
                        && board[r +2][col] == chess
                        && board[r +3][col] == chess
                        && board[r +4][col] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e){
                continue;
            }
        }
        // 3. 斜着五子连珠 -> 左上到右下(左对角线)
        for (int r = row - 4, c = col - 4; r <= row && c <= col; c++, r++){
            try {
                if (board[r][c] == chess
                        && board[r +1][c +1] == chess
                        && board[r +2][c +2] == chess
                        && board[r +3][c +3] == chess
                        && board[r +4][c +4] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }
        // 4. 斜着五子连珠 -> 右上到左下(右对角线)
        for (int r = row - 4, c = col + 4; r <= row && c >= col; r++, c--) {
            try {
                if (board[r][c] == chess
                        && board[r + 1][c - 1] == chess
                        && board[r + 2][c - 2] == chess
                        && board[r + 3][c - 3] == chess
                        && board[r + 4][c - 4] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        // 胜负未分返回0
        return 0;
    }

    public static void main(String[] args) {
        Room room = new Room();
        System.out.println(room.roomId);
    }
}
